<!--
 * @Author: Vee (547966838@qq.com)
 * @Date: 2024-08-08 13:44:11
 * @LastEditors: Vee (547966838@qq.com)
 * @LastEditTime: 2024-08-09 15:10:29
 * @Descripttion: 
-->
<template>
    <div>
        <el-button type="primary" @click="useCamera()">录入人脸</el-button>
        <el-button type="primary" @click="useCamera2()">开始识别 (当前通过相似度为：小于{{ minConfidence }})</el-button>
        <el-button type="default" @click="photoShoot">手动拍照（如果无法识别到人脸情况下使用）</el-button>
        <el-alert
            :title="httpsAlert"
            type="info"
            :closable="false"
            v-show="httpsAlert !== ''">
        </el-alert>
        <div class="videoImage" ref="faceBox">
            <video ref="video" style="display: none;"></video>
            <canvas ref="canvas" width="400" height="400"></canvas>
            <img style="background-color: #fff;" width="400" height="400" ref="image" :src="picture" v-show="pictureShow">
        </div>
    </div>
</template>
<script setup lang="ts">
import * as faceApi from 'face-api.js'
import { onMounted, onUnmounted, ref } from 'vue'
import axios from 'axios'
const minConfidence = ref(0.01) // 精确度
let videoShow = ref<boolean>(false)
let pictureShow= ref<boolean>(false)
let picture= ref<any>('')
let canvas= ref(null)
let video= ref(null)
let image= ref<any>(null)
let options= ref<any>('')
let noOne= ref<any>('')
let moreThanOne= ref<any>('')
let httpsAlert= ref<string>('')
const faceBox = ref<any>(null)
let timeout = null
// 人脸特征值数据
let faceInfo = ref<any>(null)
let recognize = ref<boolean>(false)

const init = async () => {
    await faceApi.nets.ssdMobilenetv1.loadFromUri("/models");
    await faceApi.loadFaceLandmarkModel("/models");
    options.value = new faceApi.SsdMobilenetv1Options({
        minConfidence: 0.5, // 0.1 ~ 0.9
    });
}

const useCamera = () =>{
    httpsAlert.value = ''
    recognize.value = false
    videoShow.value = true
    pictureShow.value = false
    cameraOptions()
}

const useCamera2 = () =>{
    httpsAlert.value = ''
    recognize.value = true
    videoShow.value = true
    pictureShow.value = false
    cameraOptions()
}

const cameraOptions= () => {
    let constraints = {
        video: {
            width: 400,
            height: 400
        }
    }
    // 如果不是通过loacalhost或者通过https访问会将报错捕获并提示
    try{
        let promise = navigator.mediaDevices.getUserMedia(constraints);
        promise.then((MediaStream) => {
            // 返回参数
            video.value.srcObject = MediaStream;
            video.value.play();
            recognizeFace()
        }).catch((error) => {
            console.error('getUserMediaError',error);
        });
    }catch(err){
        httpsAlert.value = `您现在在使用非Https访问，
        请先在chrome://flags/#unsafely-treat-insecure-origin-as-secure中修改配置,
        添将当前链接${window.location.href}添加到列表,
        并且将Insecure origins treated as secure修改为enabled,
        修改完成后请重启浏览器后再次访问！`
    }
}

const recognizeFace = async () => {
    if (video.value.paused) return clearTimeout(timeout);
    canvas.value.getContext('2d', { willReadFrequently: true }).drawImage(video.value, 0, 0, 400, 400);
    const results = await faceApi.detectAllFaces(canvas.value, options.value).withFaceLandmarks();
    if(results.length === 0){
        if(moreThanOne.value !== ''){
            moreThanOne.value.close()
            moreThanOne.value = ''
        }
        if(noOne.value === ''){
            console.log('未检测到人脸')
            httpsAlert.value = '未检测到人脸'
        }
    }else if(results.length > 1){
        if(noOne.value !== ''){
            noOne.value.close()
            noOne.value = ''
        }
        if(moreThanOne.value === ''){
            console.log('检测到多个人脸')
            httpsAlert.value = '检测到多个人脸'
        }
    }else{
        if(noOne.value !== ''){
            noOne.value.close()
            noOne.value = ''
        }
        if(moreThanOne.value !== ''){
            moreThanOne.value.close()
            moreThanOne.value = ''
        }
        if(!recognize.value){
            // 录入人脸
            faceInfo.value = results[0]
            photoShoot()
            canvas.value.getContext('2d')
            canvas.value.height = canvas.value.height
            httpsAlert.value = ''
        }else{
            if(!faceInfo.value){
                httpsAlert.value = '请先录入人脸'
                return
            }
            // 识别人脸
            let face = results[0]
            // 计算相似度
            let similarity = parseFloat(face.detection.score as any) - parseFloat(faceInfo.value.detection.score)
            httpsAlert.value = `相似度：${similarity}`
            // similarity取绝对值
            if(Math.abs(similarity) < minConfidence.value){
                // 停止摄像头成像
                video.value.srcObject.getTracks()[0].stop()
                video.value.pause()
                canvas.value.getContext('2d')
                canvas.value.height = canvas.value.height
                httpsAlert.value = '识别成功！' + httpsAlert.value
            }
        }
    }
    timeout = setTimeout(() => {
        return recognizeFace()
    });
}

const photoShoot = async () => {
    // 拿到图片的base64
    let cvs = canvas.value.toDataURL("image/png");
    // 拍照以后将video隐藏
    videoShow.value = false
    pictureShow.value = true
    // 停止摄像头成像
    video.value.srcObject.getTracks()[0].stop()
    video.value.pause()
    if(cvs) {
        // 拍照将base64转为file流文件
        let blob = dataURLtoBlob(cvs);
        let file = blobToFile(blob, "imgName");
        // 将blob图片转化路径图片
        let image = window.URL.createObjectURL(file)
        picture.value = image
        return
        let formData = new FormData()
        formData.append('file', picture.value)
        axios({
            method: 'post',
            url: '/user/12345',
            data: formData
        }).then(res => {
            console.log(res)
        }).catch(err => {
            console.log(err)
        })
    } else {
        console.log('canvas生成失败')
    }
}

const dataURLtoBlob = (dataurl) => {
    let arr = dataurl.split(','),
        mime = arr[0].match(/:(.*?);/)[1],
        bstr = atob(arr[1]),
        n = bstr.length,
        u8arr = new Uint8Array(n);
    while(n--) {
        u8arr[n] = bstr.charCodeAt(n);
    }
    return new Blob([u8arr], {
        type: mime
    });
}
const blobToFile = (theBlob, fileName) => {
    theBlob.lastModifiedDate = new Date().toLocaleDateString();
    theBlob.name = fileName;
    return theBlob;
}

onMounted(()=>{
    init()
})
onUnmounted(()=>{
    clearTimeout(timeout)
})
</script>
<style scoped lang="scss">
</style>